home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QRZ! Ham Radio 3
/
QRZ Ham Radio Callsign Database - Volume 3.iso
/
world
/
mac
/
space
/
satquizr.hqx
/
SATQuiz.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-09
|
12KB
|
492 lines
/* Performs the following functions.
1) Allows the user to input data through a text file.
2) Reads in data from the SAT.dat file
3) "Randomizes" the data
4) Saves the data in randomized order
5) Gives the user a multiple choice quiz
SAT Quizzer 0.3 is written by Phil Sarin, and is a complete recoding of SAT
Quizzer 0.22, also by Phil Sarin.
I guess it's time for me to explain how this works. The program uses three
main methods of storing and accessing data.
1) The array of linked lists. (allwords vocabulary)
I use this structure in main(), initwords(), getdatafile(),
gettextfile(), and assemblelist(). The constant NUMSLOTS
determines the size of the array. This structure might bring
the idea of chaining (in hash functions) to mind. That is how
I thought it up.
I wanted to be able to use the computer- generated random
numbers for a more randomized effect. However, if I simply
picked a random number every time the program asked a
question, I would have problems..especially since every
question should be asked only once. Imagine after
question 392 out of 393. The program would have to keep
picking random numbers until it found the array index of the
one word which had not been asked.
So, I thought up the chaining method. Now, there are NUMSLOTS
(say 10 for example) array indices which each have a linked
list. When a random number is generated, I just take an item
off of chain 0 - 9, depending on the number. This way, when
word 392 out of 393 has been asked, the computer only needs to
generate numbers until the right number out of 10 is chosen.
The advantage: it's a lot quicker to find 1/10 than 1/393.
2) The singly linked list. assemblelist(), putdatafile(), and quiz().
(wordlist listofwords)
Before the program even starts asking questions, it takes the
array of linked lists and starts generating random numbers
between 0 and 9. Each time, one item is removed from a
corresponding chain, and placed on a singly linked list. This
list is written back to disk. Thus, it is written back in a
different order than it was read in.
The singly linked lists is a very programmer-friendly and easy
structure to use. It was very easy to insert items on it in
assemblelist(). Additionally, both putdatafile(), and quiz()
simply went word to word down the entire list. The entire
thing is dynamic, so the limitations are purely hardware
dependent. I used this structure because of the ease of
insertion, and the ease of scrolling through the data.
3) The dynamic array. quiz(), and showchoices() (ptrarray nodeptrs)
This structure helped me overcome the limitations with earlier
versions of SAT Quizzer. In order to generate multiple choice
questions with any tolerable amount of speed, I needed to be
able to somehow index into my data. Indexing is virtually
impossible with a linked list. In the earlier Pascal versions
of SAT Quizzer, I had used an array of pointers to overcome
this problem. In Pascal, I had to set a maximum limit to the
number of words allowed in an array. In C, using calloc(), I
overcame this problem. This dynamic array would also be an
array of pointers. Each slot in the dynamic array would point
to a corresponding node in the singly linked list. This way,
in showchoices(), I could generate a random number between
1 and numwords, and easily index into it using the dynamic
array.
I can't possibly go over every nuance of every function. The
explanation of the data structures should clarify most questions one
might have about the program.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "defines.h"
#include "typedefs.h"
#include "prototypes.h"
int currentslot = 0; /* For getdatafile and gettextfile. */
int numwords = 0;
void main(void)
{
allwords vocabulary;
wordlist listofwords;
initwords(vocabulary);
initrandom();
printf("Would you like to use the words which are already in SAT");
printf("Quizzer\'s database? ");
if(toupper(getchln()) != 'N')
getdatafile(vocabulary);
printf("Would you like to input words through a text file? ");
if(toupper(getchln()) != 'N')
gettextfile(vocabulary);
listofwords = assemblelist(vocabulary);
putdatafile(listofwords);
quiz(listofwords);
}
char getchln(void)
/* Returns a char read from the keyboard and flushes an EOLN if the char is
not an EOLN. */
{
char typedin, returnval;
typedin = returnval = getchar();
while(typedin != '\n')
typedin = getchar();
return returnval;
}
void initwords(allwords newwords)
/* Initializes every slot in the newwords array to NULL. */
{
int i;
for(i = 0; i < NUMSLOTS; i++)
newwords[i] = NULL;
}
void initrandom(void)
/* Initializes the rand() function with a clock seed. */
{
int utime;
long ltime;
ltime = time(NULL);
utime = (unsigned int) ltime / 2;
srand(utime);
}
void getdatafile(allwords vocabulary)
/* Reads in vocabulary from a binary data file. */
{
FILE *infile;
wordlist currentword;
struct entry wordfromfile; /* Used for debugging. I'll leave it in. */
putchar('\n');
printf("Opening data file...\n");
if((infile = fopen(DATAPATH, "rb")) == NULL)
printf("Cannot open file. A new file will be made.\n");
else {
printf("Reading...\n");
while(!feof(infile)) {
if(fread(&wordfromfile, sizeof wordfromfile, 1,infile)
!= 1)
break; /* Don't know why, but EOF is
unexpectedly encountered here.
That's why there's a break, and not
an exit. */
currentword = wordalloc();
currentword->info = wordfromfile;
currentword->next = vocabulary[currentslot];
vocabulary[currentslot] = currentword;
currentslot = (currentslot + 1) % NUMSLOTS;
}
}
printf("Closing data file...\n\n");
fclose(infile);
}
void gettextfile(allwords vocabulary)
/* Reads in vocabulary from a text file created as such:
partofspeech,word,definition
i.e.
adj,loud,noisy
*/
{
FILE *textfile;
char textpath[20], inchar;
wordlist currentword;
int currentletter;
printf("Please type in the name of the text file. ");
gets(textpath);
printf("\nOpening text file...\n");
if((textfile = fopen(textpath, "r")) == NULL)
printf("Could not open file.\n");
else {
printf("Reading...\n");
while(!feof(textfile)) {
currentword = wordalloc();
/**** Find part of speech. ****/
fgetc(textfile);
fgetc(textfile);
/* The third letters are all distinct. */
switch(toupper(fgetc(textfile))) {
case 'U' :
currentword->info.partofspeech = noun;
break;
case 'R' :
currentword->info.partofspeech = verb;
break;
case 'J' :
currentword->info.partofspeech = adj;
break;
default :
currentword->info.partofspeech = other;
}
/* Flush out until comma. */
while(fgetc(textfile) != ',') ;
/**** Get word. ****/
for(inchar = fgetc(textfile), currentletter = 0;
inchar != ',' && currentletter < MAXWORD;
inchar = fgetc(textfile), currentletter++)
currentword->info.word[currentletter] =
inchar;
/* The loop inputs char by char into the word until
a comma is encountered. */
currentword->info.word[currentletter] = '\0';
/**** Get meaning. ****/
fgets(currentword->info.definition, MAXMEANING,
textfile);
currentletter = strlen(currentword->info.definition);
/* Check to see if letters were truncated because the
string in the data file was too long. Flush to
a new line if they were. If they weren't, remove
the '\n' from the string and replace with '\0'. */
if(currentword->info.definition[currentletter-1]=='\n')
currentword->info.definition[currentletter - 1]
= '\0';
else
while(fgetc(textfile) != '\n' &&
!feof(textfile))
;
/**** Attach word to data structure. ****/
currentword->next = vocabulary[currentslot];
vocabulary[currentslot] = currentword;
currentslot = (currentslot + 1) % NUMSLOTS;
}
}
printf("Closing text file...\n\n");
fclose(textfile);
}
wordlist wordalloc()
/* Allocates enough space for a data object of type struct node. */
{
return (wordlist) malloc(sizeof(struct node));
}
wordlist assemblelist(allwords vocabulary)
/* Returns the pointer to a linked list. */
{
wordlist nextnode, listsofar = NULL;
short done, slottouse;
printf("\nRandomizing...\n");
for(slottouse = 0, done = 1; slottouse < NUMSLOTS && done; slottouse++)
done = vocabulary[slottouse] == NULL;
while(!done) {
for(slottouse = rand() % NUMSLOTS; vocabulary[slottouse] ==
NULL; slottouse = rand() % NUMSLOTS) ;
numwords++;
nextnode = vocabulary[slottouse];
vocabulary[slottouse] = vocabulary[slottouse]->next;
nextnode->next = listsofar;
listsofar = nextnode;
for(slottouse = 0, done = 1; slottouse < NUMSLOTS && done;
slottouse++)
done = vocabulary[slottouse] == NULL;
}
return listsofar;
}
void putdatafile(wordlist listofwords)
/* Saves the data file to disk. */
{
FILE *outfile;
printf("\nOpening file...\n");
if((outfile = fopen(DATAPATH, "wb")) == NULL) {
printf("Error opening file.\n");
exit(1);
}
printf("Writing...\n");
while(listofwords) {
if(fwrite(&(listofwords->info), sizeof listofwords->info, 1,
outfile) != 1) {
printf("Error writing to disk.\n");
exit(1);
}
listofwords = listofwords->next;
}
printf("Closing file...\n\n");
fclose(outfile);
}
void quiz(wordlist listofwords)
/* Generates multiple choice questions. */
{
char rightanswer, answer;
ptrarray nodeptrs, p;
wordlist q;
int right = 0, numdone = 0;
nodeptrs = calloc(numwords, sizeof(wordlist));
for(p = nodeptrs, q = listofwords; q; p++, q = q->next)
*p = q;
while(listofwords) {
printpos(listofwords->info.partofspeech);
printf(". %s\n", listofwords->info.word);
putchar('\n');
rightanswer = showchoices(nodeptrs,
listofwords->info.definition, listofwords->
info.partofspeech);
printf("\nAnswer: ('a' - 'd', 'q' to quit) ");
if((answer = tolower(getchln())) == 'q')
break;
numdone++;
if(answer == rightanswer) {
printf("Right!\n");
++right;
}
else
printf("Sorry, the answer is %c.\n", rightanswer);
putchar('\n');
listofwords = listofwords->next;
}
printf("Thanks for using SAT Quizzer. You got %d out of %d right.\n",
right, numdone);
}
void printpos(postype postoprint)
{
switch(postoprint) {
case noun :
printf("noun");
break;
case verb :
printf("verb");
break;
case adj :
printf("adj");
break;
case other :
printf("adv. or prep");
break;
}
}
char showchoices(ptrarray nodeptrs, meaningtype correctmeaning, postype
correctpos)
/* Prints out all the multiple choice options which the user will have. This
procedure is not very intuitively named unfortunately. It returns the letter
of the correct answer. */
{
short ok, i, j, answernum, randindex;
meaningtype options[NUMOPTIONS], testmeaning;
ptrarray p;
postype testpos;
for(i = 0; i < NUMOPTIONS; i++)
options[i][0] = '\0';
answernum = rand() % 4;
strcpy(options[answernum], correctmeaning);
/* Fill in "dummy" choices and print them out. */
for(i = 0, ok = 0; i < NUMOPTIONS; i++, ok = 0) {
if(i != answernum) {
while(!ok) {
randindex = rand() % numwords;
p = nodeptrs + randindex;
strcpy(testmeaning, (*p)->info.definition);
testpos = (*p)->info.partofspeech;
for(j = 0, ok = testpos == correctpos; j < i &&
ok; j++)
ok = strcmp(testmeaning, options[j]) &&
testpos == correctpos;
ok = ok && strcmp(testmeaning,
options[answernum]);
}
strcpy(options[i], testmeaning);
}
printf("\t%c. %s\n", (char) (i + 'a'), options[i]);
}
return (char) (answernum + 'a');
}